#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <string.h>
#include <signal.h>

#ifdef DEBUG
#include <stdio.h>
#endif

#include "config.h"
#include "choose.h"
#include "random.h"
#include "findhome.h"
#include "blacklist.h"

/*
 * A standard signal handler, which catches SIGTERM 
 * and cleans us up. 
 */
void handlesigterm (int signal);

/*
 * This will be set to 0 on receiving SIGTERM, 
 * informing the main loop to exit.
 */
volatile int running = 1;

/*
 * Paths to the PID files. The signal handler needs 
 * at least one of these.
 */
char main_pid_file [32];
char pid_file [32];

int main (int argc, char **argv, char **env)
{
	int status;
	int fd;
	pid_t main_pid;
	pid_t pid;
	uint64_t seed;
	char *home;
	char music_path [32];
	char song_path [256];
	struct sigaction termaction;
	struct stat file_info;
	struct timeval timeval;
#ifdef DEBUG
	FILE *file;
	char log_path [32];
#endif

	(void) argc;
	(void) argv;

#ifndef DEBUG
	/*
	 * Daemonize.
	 */
	pid = fork ();
	if (pid != 0)
	{
		exit (0);
	}
#endif

	/*
	 * Find the home directory ~.
	 */
	home = findhome (env);
	
	/*
	 * Find the paths to the PID files.
	 */
	strncpy (main_pid_file, home, 32);
	strncat (main_pid_file, MAIN_PID_FILE, 32);
	strncpy (pid_file, home, 32);
	strncat (pid_file, PID_FILE, 32);

	/*
	 * Check whether the another process is already running.
	 */
	status = stat (main_pid_file, &file_info);
	if (status == 0)
	{
		exit (0);
	}

	/*
	 * Find the full path to ~/Music.
	 */
	strncpy (music_path, home, 32);
	strncat (music_path, MUSIC_DIR, 32);

#ifdef DEBUG
	/*
	 * Find the path to the log file.
	 */
	strncpy (log_path, home, 32);
	strncat (log_path, LOG_FILE, 32);

	/*
	 * Remove any old log file.
	 */
	unlink (log_path);
#endif

	/*
	 * Set up the handler for SIGTERM.
	 */
	termaction.sa_handler = handlesigterm;
	sigaction (SIGTERM, &termaction, 0);

	/*
	 * Save the PID.
	 */
	main_pid = getpid ();
	fd = open (main_pid_file, O_WRONLY | O_CREAT, 0644);
	write (fd, &main_pid, sizeof(pid_t));
	close (fd);

	/*
	 * Initialize the blacklist
	 */
	initialize_blacklist (home);

	/*
	 * Initialize and seed the random number generator.
	 */
	initialize_random (home);
	fd = open (DEV_RANDOM, O_RDONLY);
	read (fd, &seed, sizeof (seed));
	close (fd);
	seed_random (seed);

	/*
	 * Select and play songs from ~/Music
	 */
	while (running)
	{

		/*
		 * Pick a song
		 */
		choose (music_path, song_path);

#ifdef DEBUG
		/*
		 * Log it
		 */
		file = fopen (log_path, "a");
		fprintf (file, "%s \n", song_path);
		fclose (file);
#endif

		/*
		 * fork an play it.
		 */
		pid = fork ();
		if (pid == 0)
		{
			/*
			 * This is the child process.
			 */
			close (0);
			close (1);
			close (2);
			execl (PLAY, PLAY, song_path, NULL);
		}
		else
		{
			/*
			 * This is the parent process.
			 */

			/*
			 * Get the current time in usec.
			 * Use it as entropy.
			 */
			gettimeofday (&timeval, 0);
			seed = (uint64_t) timeval.tv_usec;
			seed_random (seed);

			/*
			 * Save the child PID.
			 */
			fd = open (pid_file, O_WRONLY | O_CREAT, 0644);
			write (fd, &pid, sizeof(pid_t));
			close (fd);
			
			/*
			 * Wait until the child process finishes.
			 */
			waitpid (pid, &status, 0);
		}
	}

	/*
	 * We've caught a SIGTERM. Clean up.
	 */
	finalize_random (home);
	finalize_blacklist (home);
	unlink (main_pid_file);
	unlink (pid_file);

	return 0;
}

/*
 
_|_
 |
 |

*/

void handlesigterm (int signal)
{
	pid_t pid;
	int fd;

	(void) signal;

	/*
	 * Tell the main loop to terminate at its earliest convenience.
	 */
	running = 0;

	/*
	 * Kill the child process. 
	 */
	fd = open (pid_file, O_RDONLY);
	read (fd, &pid, sizeof(pid_t));
	close (fd);

	kill (pid, SIGTERM);
}

